require "stategraphs/SGtentacle"

local assets = {
	Asset("ANIM", "anim/tentacle_arm.zip"),
	Asset("ANIM", "anim/tentacle_arm_black_build.zip"),
	Asset("SOUND", "sound/tentacle.fsb"),
	Asset("ANIM", "anim/musha_tentacle_frost.zip")
}

local prefabs = {
	"monstermeat",
	"tentaclespike",
	"tentaclespots"
}

SetSharedLootTable(
	"musha_tentacle_frost",
	{
		{"ice", 0.75},
		{"ice", 0.75},
		{"ice", 0.75}
	},
	"musha_tentacle_shadow",
	{
		{"nightmarefuel", 0.02}
	}
)

local function OnHitfreeze(inst, data)
	local other = data.target
	if not (other:HasTag("smashable")) then
		if other and other.components.freezable and not other:HasTag("dragonfly") then
			other.components.freezable:AddColdness(1)
			other.components.freezable:SpawnShatterFX()
		elseif other and other.components.freezable and other:HasTag("dragonfly") then
			other.components.freezable:AddColdness(0.25)
			other.components.freezable:SpawnShatterFX()
		elseif other and other:HasTag("_musha") then
			inst.components.combat:SetTarget(nil)
			inst.components.combat:GiveUp()
		end
		if other.components.burnable and other.components.burnable:IsBurning() then
			other.components.burnable:Extinguish()
		end
	end
end

local function OnHitShadow(inst, data)
	local other = data.target
	if not (other:HasTag("smashable")) then
		if other:HasTag("_musha") then
			inst.components.combat:SetTarget(nil)
			inst.components.combat:GiveUp()
		else
			if
				inst.components.follower and inst.components.follower.leader and
					inst.components.follower.leader.components.inventoryitem and
					inst.components.follower.leader.components.inventoryitem.owner and
					inst.components.follower.leader.components.inventoryitem.owner.components.health
			 then
				inst.components.follower.leader.components.inventoryitem.owner.components.health:DoDelta(data.damage * 0.3)
			end
		end
		if other.components.burnable and other.components.burnable:IsBurning() then
			other.components.burnable:Extinguish()
		end
	end
end

local function retargetfn(inst) --#TODO: PVP
	return FindEntity(
		inst,
		TUNING.TENTACLE_ATTACK_DIST,
		function(guy)
			return guy.prefab ~= inst.prefab and guy.entity:IsVisible() and not guy.components.health:IsDead() and
				guy ~= (inst or inst.components.follower.leader) and
				not (not guy.components.follower or
					(guy.components.follower.leader and guy.components.follower.leader == inst.components.follower.leader)) and
				not guy:HasTag("player") and
				not guy:HasTag("companion") and
				not guy:HasTag("wall") -- and not guy:HasTag("musha_yamche")
		end,
		{"_combat", "_health"},
		{"prey"}
	)
	--[[ local dist = 20 return FindEntity(inst,dist,function(guy) return inst.components.combat:CanTarget(guy end,nil,{"musha","player","wall","houndmound","structure","companion","musha_yamche","beefalo","koalefant","musha_beefalo","pig","bee","rocky","bird","statue","character","abigail","smashable","veggie","shadowminion"}) ]]
end

local function shouldKeepTarget(inst, target)
	return target ~= nil and target:IsValid() and target.entity:IsVisible() and target.components.health ~= nil and
		not target.components.health:IsDead() and
		target:IsNear(inst, TUNING.TENTACLE_STOPATTACK_DIST)
end

local function OnAttacked(inst, data)
	if data.attacker == nil then
		return
	end

	local current_target = inst.components.combat.target

	if current_target == nil then
		--Don't want to handle initiating attacks here;
		--We only want to handle switching targets.
		return
	elseif current_target == data.attacker then
		--Already targeting our attacker, just update the time
		inst._last_attacker = current_target
		inst._last_attacked_time = GetTime()
		return
	end

	local time = GetTime()
	if inst._last_attacker == current_target and inst._last_attacked_time + TUNING.TENTACLE_ATTACK_AGGRO_TIMEOUT >= time then
		--Our target attacked us recently, stay on it!
		return
	end

	--Switch to new target
	inst.components.combat:SetTarget(data.attacker)
	inst._last_attacker = data.attacker
	inst._last_attacked_time = time
end

local function common_fn()
	local inst = CreateEntity()

	inst.entity:AddTransform()
	inst.entity:AddPhysics()
	inst.entity:AddAnimState()
	inst.entity:AddSoundEmitter()
	inst.entity:AddNetwork()

	inst.Physics:SetCylinder(0.25, 2)

	inst.Transform:SetScale(0.7, 0.7, 0.7)

	inst:AddTag("wet")
	inst:AddTag("companion")

	inst:AddComponent("colourtweener")
	inst.components.colourtweener:StartTween({1, 1, 1, .85}, 0)

	inst.entity:SetPristine()

	if not TheWorld.ismastersim then
		return inst
	end

	inst._last_attacker = nil
	inst._last_attacked_time = nil

	inst:AddComponent("follower")
	inst.components.follower:KeepLeaderOnAttacked()
	inst.components.follower.keepdeadleader = true

	inst:AddComponent("health")

	inst:AddComponent("hunger")
	inst.components.hunger:SetRate(1)

	inst:AddComponent("combat")
	inst.components.combat:SetRetargetFunction(GetRandomWithVariance(2, 0.5), retargetfn)
	inst.components.combat:SetKeepTargetFunction(shouldKeepTarget)
	inst.components.combat.playerdamagepercent = 0

	MakeLargeFreezableCharacter(inst)

	inst:AddComponent("inspectable")
	inst:AddComponent("lootdropper")

	inst:SetStateGraph("SGtentacle")
	inst:ListenForEvent("attacked", OnAttacked)

	return inst
end

local function tentacle_frost_fn()
	local inst = common_fn()

	inst.AnimState:SetBank("tentacle")
	inst.AnimState:SetBuild("musha_tentacle_frost")
	inst.AnimState:PlayAnimation("idle")

	inst:AddTag("musha_frost_tentacle")

	inst:AddComponent("talker")
	inst.components.talker.fontsize = 26
	inst.components.talker.colour = Vector3(20, 20, 100, 1)
	inst.components.talker:Say(math.ceil(inst.components.hunger:GetPercent() * 100) .. "%")

	if not TheWorld.ismastersim then
		return inst
	end

	inst.components.health:SetMaxHealth(500)
	inst.components.health:SetPercent(1)

	inst.components.hunger:SetMax(500)

	inst.components.combat:SetRange(TUNING.TENTACLE_ATTACK_DIST)
	inst.components.combat:SetAttackPeriod(TUNING.TENTACLE_ATTACK_PERIOD)
	inst.components.combat:SetDefaultDamage(TUNING.TENTACLE_DAMAGE * 0.75)

	inst.components.lootdropper:SetChanceLootTable("musha_tentacle_frost")

	inst:ListenForEvent("onhitother", OnHitfreeze)
	inst:ListenForEvent(
		"hungerdelta",
		function(inst)
			inst.components.talker:Say(math.ceil(inst.components.hunger:GetPercent() * 100) .. "%")
		end
	)
	inst:ListenForEvent(
		"startstarving",
		function()
			inst.components.health:Kill()
		end
	)

	return inst
end

local function tentacle_shadow_fn()
	local inst = common_fn()

	inst.AnimState:SetBank("tentacle_arm")
	inst.AnimState:SetBuild("tentacle_arm_black_build")
	inst.AnimState:PlayAnimation("idle")

	inst:AddTag("companion")
	inst:AddTag("shadow")
	inst:AddTag("notarget")
	inst:AddTag("noattack")
	inst:AddTag("NOCLICK")

	if not TheWorld.ismastersim then
		return inst
	end

	inst.components.health:SetMaxHealth(5)
	inst.components.health:SetPercent(1)

	inst.components.hunger:SetMax(90)

	inst.components.combat:SetRange(2.8)
	inst.components.combat:SetAttackPeriod(2.5)
	inst.components.combat:SetDefaultDamage(15)

	inst:ListenForEvent("onhitother", OnHitShadow)
	inst:ListenForEvent(
		"startstarving",
		function(inst)
			inst.components.health:SetInvincible(false)
			inst.components.health:Kill()
			local fx = SpawnPrefab("statue_transition")
			fx.Transform:SetScale(0.45, 0.45, 0.45)
			fx.Transform:SetPosition(inst:GetPosition():Get())
			inst:Remove()
		end
	)
	inst:ListenForEvent(
		"death",
		function()
			SpawnPrefab("statue_transition").Transform:SetPosition(inst:GetPosition():Get())
			inst.SoundEmitter:PlaySound("dontstarve/common/fireOut")
		end
	)

	inst.components.combat:SetRetargetFunction(GetRandomWithVariance(1, 0.5), retargetfn)

	inst.components.health:SetInvincible(true)
	inst:DoTaskInTime(
		10,
		function()
			inst.components.health:SetInvincible(false)
		end
	)

	inst.components.lootdropper:SetChanceLootTable("musha_tentacle_shadow")

	return inst
end

return Prefab("musha_tentacle_frost", tentacle_frost_fn, assets, prefabs), Prefab(
	"musha_tentacle_shadow",
	tentacle_shadow_fn,
	assets,
	prefabs
)
